package com.kefu.admin.common.zk.zkleader;

import org.springframework.aop.support.AopUtils;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor;
import org.springframework.scheduling.config.CronTask;
import org.springframework.scheduling.config.IntervalTask;
import org.springframework.scheduling.config.ScheduledTask;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.scheduling.support.ScheduledMethodRunnable;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.util.StringValueResolver;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.TimeZone;

/**
 * Created by TWD on 2018/9/29.
 */
public class ZkScheduledAnnotationBeanPostProcessor  extends ScheduledAnnotationBeanPostProcessor {

    private StringValueResolver embeddedValueResolver;
    private final ScheduledTaskRegistrar registrar = (ScheduledTaskRegistrar)this.getFieldValueFromParentClass("registrar");
    private final Map<Object, Set<ScheduledTask>> scheduledTasks = (Map)this.getFieldValueFromParentClass("scheduledTasks");
    private ZkSchedulerCoordinator zkSchedulerCoordinator;

    public ZkScheduledAnnotationBeanPostProcessor(ZkSchedulerCoordinator zkSchedulerCoordinator) {
        this.zkSchedulerCoordinator = zkSchedulerCoordinator;
    }

    public void setEmbeddedValueResolver(StringValueResolver resolver) {
        super.setEmbeddedValueResolver(resolver);
        this.embeddedValueResolver = resolver;
    }

    protected void processScheduled(Scheduled scheduled, Method method, Object bean) {
        try {
            Assert.isTrue(method.getParameterTypes().length == 0, "Only no-arg methods may be annotated with @Scheduled");
            Method invocableMethod = AopUtils.selectInvocableMethod(method, bean.getClass());
            Runnable runnable = null;
            if(this.hasZkLeaderScheduleAnnotation(invocableMethod, bean)) {
                runnable = new ZkScheduledMethodRunnable(bean, invocableMethod, this.zkSchedulerCoordinator);
            } else {
                runnable = new ScheduledMethodRunnable(bean, invocableMethod);
            }

            boolean processedSchedule = false;
            String errorMessage = "Exactly one of the 'cron', 'fixedDelay(String)', or 'fixedRate(String)' attributes is required";
            Set<ScheduledTask> tasks = new LinkedHashSet(4);
            long initialDelay = scheduled.initialDelay();
            String initialDelayString = scheduled.initialDelayString();
            if(StringUtils.hasText(initialDelayString)) {
                Assert.isTrue(initialDelay < 0L, "Specify 'initialDelay' or 'initialDelayString', not both");
                if(this.embeddedValueResolver != null) {
                    initialDelayString = this.embeddedValueResolver.resolveStringValue(initialDelayString);
                }

                try {
                    initialDelay = Long.parseLong(initialDelayString);
                } catch (NumberFormatException var25) {
                    throw new IllegalArgumentException("Invalid initialDelayString value \"" + initialDelayString + "\" - cannot parse into integer");
                }
            }

            String cron = scheduled.cron();
            if(StringUtils.hasText(cron)) {
                Assert.isTrue(initialDelay == -1L, "'initialDelay' not supported for cron triggers");
                processedSchedule = true;
                String zone = scheduled.zone();
                if(this.embeddedValueResolver != null) {
                    cron = this.embeddedValueResolver.resolveStringValue(cron);
                    zone = this.embeddedValueResolver.resolveStringValue(zone);
                }

                TimeZone timeZone;
                if(StringUtils.hasText(zone)) {
                    timeZone = StringUtils.parseTimeZoneString(zone);
                } else {
                    timeZone = TimeZone.getDefault();
                }

                tasks.add(this.registrar.scheduleCronTask(new CronTask((Runnable)runnable, new CronTrigger(cron, timeZone))));
            }

            if(initialDelay < 0L) {
                initialDelay = 0L;
            }

            long fixedDelay = scheduled.fixedDelay();
            if(fixedDelay >= 0L) {
                Assert.isTrue(!processedSchedule, errorMessage);
                processedSchedule = true;
                tasks.add(this.registrar.scheduleFixedDelayTask(new IntervalTask((Runnable)runnable, fixedDelay, initialDelay)));
            }

            String fixedDelayString = scheduled.fixedDelayString();
            if(StringUtils.hasText(fixedDelayString)) {
                Assert.isTrue(!processedSchedule, errorMessage);
                processedSchedule = true;
                if(this.embeddedValueResolver != null) {
                    fixedDelayString = this.embeddedValueResolver.resolveStringValue(fixedDelayString);
                }

                try {
                    fixedDelay = Long.parseLong(fixedDelayString);
                } catch (NumberFormatException var24) {
                    throw new IllegalArgumentException("Invalid fixedDelayString value \"" + fixedDelayString + "\" - cannot parse into integer");
                }

                tasks.add(this.registrar.scheduleFixedDelayTask(new IntervalTask((Runnable)runnable, fixedDelay, initialDelay)));
            }

            long fixedRate = scheduled.fixedRate();
            if(fixedRate >= 0L) {
                Assert.isTrue(!processedSchedule, errorMessage);
                processedSchedule = true;
                tasks.add(this.registrar.scheduleFixedRateTask(new IntervalTask((Runnable)runnable, fixedRate, initialDelay)));
            }

            String fixedRateString = scheduled.fixedRateString();
            if(StringUtils.hasText(fixedRateString)) {
                Assert.isTrue(!processedSchedule, errorMessage);
                processedSchedule = true;
                if(this.embeddedValueResolver != null) {
                    fixedRateString = this.embeddedValueResolver.resolveStringValue(fixedRateString);
                }

                try {
                    fixedRate = Long.parseLong(fixedRateString);
                } catch (NumberFormatException var23) {
                    throw new IllegalArgumentException("Invalid fixedRateString value \"" + fixedRateString + "\" - cannot parse into integer");
                }

                tasks.add(this.registrar.scheduleFixedRateTask(new IntervalTask((Runnable)runnable, fixedRate, initialDelay)));
            }

            Assert.isTrue(processedSchedule, errorMessage);
            Map var19 = this.scheduledTasks;
            synchronized(this.scheduledTasks) {
                Set<ScheduledTask> registeredTasks = (Set)this.scheduledTasks.get(bean);
                if(registeredTasks == null) {
                    registeredTasks = new LinkedHashSet(4);
                    this.scheduledTasks.put(bean, registeredTasks);
                }

                ((Set)registeredTasks).addAll(tasks);
            }
        } catch (IllegalArgumentException var26) {
            throw new IllegalStateException("Encountered invalid @Scheduled method '" + method.getName() + "': " + var26.getMessage());
        }
    }

    private boolean hasZkLeaderScheduleAnnotation(Method invocableMethod, Object bean) {
        return invocableMethod.isAnnotationPresent(ZkLeaderSchedule.class);
    }

    private Object getFieldValueFromParentClass(String fieldName) {
        try {
            Field field = ScheduledAnnotationBeanPostProcessor.class.getDeclaredField(fieldName);
            field.setAccessible(true);
            Object fieldValue = field.get(this);
            return fieldValue;
        } catch (Exception var4) {
            this.logger.error("通过反射读取ScheduledAnnotationBeanPostProcessor.{} 的时候出现错误", var4);
            throw new IllegalArgumentException(var4);
        }
    }
}
